package repository;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.FileFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import repository.exception.*;

/**
 * Класс репозитория.
 * Содержет функции работы с репоториями пользователей.
 * Позволяет проводить настройку и работать с файлами пользователей.
 * Все репозитории располагаются в общей корневой директории.
 * Корневая директория настраивается пользователем класса или
 * создается по-умолчанию: папка "repositories" в директории приложения.
 * Каждому репозиторию соответствует отдельная диретория.
 * Идентификатор пользователя является именем его репозитория.
 * Для работы с репозиторием необходимо знать идентификатор пользователя
 * @author Aleksey & rd
 * @version alpha
 */
public class Repository implements IRepository {

    //корневая диретория
    private File rootPath;
    //параметры репозитория
    private ConcurrentHashMap<LongComparable,RepositoryParams> rParams;

    
    //  пустой фильтр, пропускающий все
    /*private class NullFilter implements FilenameFilter{
    	@Override
		public boolean accept(File dir, String name) {
			return true;
		}
    }*/

    /**
     * Проверка установки корня репозитория
     * @throws repository.exception.NullRootException не установлен корень
     */
    private void checkRoot() throws NullRootException{
        if(rootPath==null)
            throw new NullRootException("Doesn't set repository root directory");
    }

    /**
     * Проверка регистрации репозитория пользователя
     * @param userID идентификатор пользователя
     * @throws repository.exception.NoRepositoryException не установлен репозиторий
     */
    private void checkRepository(long userID) throws NoRepositoryException{
        if(rParams.get(new LongComparable(userID)) == null)
            throw new NoRepositoryException("Repository for userID="+userID+
                    " doesn't exist");
    }

    /**
     * Запросить файл по относительному пути
     * @param userID иденитификатор пользователя
     * @param relativePath путь относительно репозитория пользователя
     * @return файл или директория
     * @throws repository.exception.NullRootException корневая директория не задана
     * @throws repository.exception.NoRepositoryException отсутствует репозиторий
     */
    public File getFile(long userID,String relativePath)
            throws NullRootException,NoRepositoryException {
        checkRoot(); //проверить, что rootPath!=null
        checkRepository(userID); //проверить наличие репозитория
        if(relativePath==null) relativePath="";
        //путь: rootPath/userID/relativePath
        return new File(rootPath.getPath()+File.separator+
                userID+File.separator+relativePath);
    }

    /**
     * Запросить путь до файла по относительному пути
     * @param userID иденитификатор пользователя
     * @param relativePath путь относительно репозитория пользователя
     * @return путь до файла
     * @throws repository.exception.NullRootException корневая директория не задана
     * @throws repository.exception.NoRepositoryException отсутствует репозиторий
     */
    public String getFilePath(long userID,String relativePath)
            throws NullRootException,NoRepositoryException{
        checkRoot(); //проверить, что rootPath!=null
        checkRepository(userID); //проверить наличие репозитория
        if(relativePath==null) relativePath="";
        //путь: rootPath/userID/relativePath
        return rootPath.getPath()+File.separator+
                userID+File.separator+relativePath;
    }

    /**
     * рекурсивное удаление
     * @param dir удаляемая директория
     * @return признак успешного/неудачного удаления
     */
    private static boolean recursiveDelete(File dir) {
        //если директория - удалить содержимое
        if (dir.isDirectory()) {
            //запросить список вложенных файлов и диреторий
            String[] children = dir.list();
            //удалить содержимое
            for (int i=0; i<children.length; i++) {
                boolean success = recursiveDelete(new File(dir, children[i]));
                //проверка на удачное удаление
                if (!success) { return false; }
            }
        }
        //удалить файл
        return dir.delete();
    }
    
    /**
     * Копировать файл
     * @param src исходный файл
     * @param dst новый файл
     * @throws java.io.IOException
     */
    private static void copy(File src, File dst) throws IOException {
        //создать файловые потоки
        InputStream in = new FileInputStream(src);
        OutputStream out = new FileOutputStream(dst);
        //побайтно копировать файл
        try{
            byte[] buf = new byte[1024];
            int len;
            while ((len = in.read(buf)) > 0) 
                out.write(buf, 0, len);
        }
        //закрыть файлы
        finally{         
            in.close();
            out.close();
        }
    }
    
    /**
     * Запросить содержимое директории, включая содержимое вложенных директорий
     * @param dir - директория
     * @param filter - фильтр
     * @param addDirectories - добавлять директории в выборку
     * @param addFiles - добавлять файлы в выборку
     * @return массив директорий
     */
   /* private List<File> getFileList(File dir, FilenameFilter filter,
            boolean addDirectories, boolean addFiles){
        //массив для выборки
    	ArrayList<File> list= new ArrayList<File>();
        //запросить содержимое директории
        String[] children = dir.list();
        File tmp;
        //обход по содержимому
        for (int i=0; i<children.length; i++) {
        	tmp=new File(dir,children[i]);
            //если содержимое - директория, получить её содержимое
        	if(tmp.isDirectory()){
                //рекурсивный вызов
        		list.addAll(getFileList(tmp,filter,addDirectories,addFiles));
                //проверить необходимость добавления директорий в выборку
        		if(addDirectories)
        			if(filter.accept(dir, children[i]))
                		list.add(tmp);
        	}
            else{
                //проверить необходимость добавления файлов в выборку
                if(addFiles)
                    if(filter.accept(dir, children[i]))
                        list.add(tmp);
            }
        }
        //верныть выборку
        return list;
    }*/

    /**
     * Запросить число файлов в папке и подпапках
     * @param path путь до иректории
     * @return число вложенных файлов
     */
    private static int innerGetFileNum(File path){
        int size=0;
        //обход содержимого
        for(File f:path.listFiles())
            //если директория - рекурсивняй вызов
            if(path.isDirectory()) size+=innerGetFileNum(f);
    	return size;
    }

    /**
     * Запросить размер файла или директории
     * @param path проверяемый файл или директория
     * @return размер в байтах
     */
    private static long innerGetSize(File path){
    	long size=0;
        //если обнарудена директория - ракурсивный вызов
        if(path.isDirectory())
            //обход содержимого
            for(File f:path.listFiles())  size+=innerGetSize(f);
        else size+=path.length();
    	return size;
    }
    
    /**
     * конструктор по умолчанию.
     * Корневая директория не установлена.
     * Для загрузки данных репозитория вызвать метод loadRepositoryData().
     * Для установки корневой директории вызвать метод setRootPath().
     * Для установки стандартной корневой директории вызвать initRootPath()
     */
    public Repository () {
        rootPath=null;
        rParams=new ConcurrentHashMap<LongComparable,RepositoryParams> ();
    }

    /**
     * конструктор с указанием корневой директории.
     * Для проверки файловой структуры репозитория вызвать checkWholeness().
     * Для загрузки данных репозитория вызвать метод loadRepositoryData().
     * @param path корневая директория. Если передать null, сгенерирует
     * NullPointerException
     * @throws repository.exception.NonDirPathException path не директория
     */
    public Repository (File path)  throws NonDirPathException {
        //проверки на null и на то, что path - директория
        if(path==null) 
            throw new NullPointerException("'path' can't be a null-value");
        if(!path.isDirectory()) 
            throw new NonDirPathException("'path' must be a directory");
        rootPath=path;
        //создать хеш параметров
        rParams=new ConcurrentHashMap<LongComparable,RepositoryParams> ();
    }

    /**
     * Метод НЕ переносит содержимое репозитория.
     * Если необходимо воссоздать структуру репозитория вызывать checkWholeness()
     * @param path корневая директория
     */
    @Override
    public void setRootPath (File path) {
        if(path==null)
            throw new NullPointerException("'path' can't be a null-value");
        rootPath=path;
    }

    /**
     * Запросить корневую директорию всех репозиториев
     * @return корневая директория
     */
    @Override
    public File getRootPath () {
        return rootPath;
    }

    /**
     * Установить корневую директорию по-умолчанию: "./repositories".
     * Метод НЕ создает директорию в случае её отсутствия.
     * Для проверки структуры репозитория вызвать метод checkWholeness().
     */
    @Override
    public void initRootPath () {
        rootPath= new File("./repositories");
    }
    
    /**
     * Проверить целостность репозитория. Проверяется наличие корневой диретории
     * и директорий пользователей. Отсутствующие диретории будут созданы.
     * @throws repository.exception.NullRootException - не задан корень
     * @throws java.io.IOException - ошибка при создании директорий
     */
    public void checkWholeness() throws NullRootException,IOException{
        checkRoot(); //проверить, что rootPath!=null
        //проверить наличие корневой директории
        if(!rootPath.exists())
            if(!rootPath.mkdirs())
                throw new IOException("can't create root directory");
        //проверить директории пользователей
        File tempF;
        //пройти по всем ключам
        Set <LongComparable> keys = rParams.keySet();    
        for(LongComparable key : keys){
            tempF=new File(rootPath.getPath(),""+key.value);
            //проверить наличие директории пользователя
            if(!tempF.exists())
                if(!tempF.mkdir())
                    throw new IOException("can't create user's directory. ID="+
                            key.value);
        }
    }

    /**
     * создать репозиторий пользователя
     * @param userID идентификатор пользователя
     * @return true в случае успеха, false если репозиторий существует или
     * userID<0
     * @throws repository.exception.NullRootException не установлена корневая
     * директория
     * @throws repository.exception.CreateDirException ошибка при создании
     * директории репозитория
     */
    @Override
    public boolean createRepository (long userID) 
            throws NullRootException,CreateDirException{
        //создать пустые параметры
        RepositoryParams tmpParams = new RepositoryParams ();
        return createRepository(userID, tmpParams);
    }

    /**
     * создать репозиторий пользователя
     * @param userID идентификатор пользователя
     * @param params параметры репозитория. В случае null будут заданы умолчания
     * @return true в случае успеха, false если репозиторий существует или
     * userID<0
     * @throws repository.exception.NullRootException не установлена корневая
     * директория
     * @throws repository.exception.CreateDirException ошибка при создании
     * директории репозитория
     */
    @Override
    public boolean createRepository (long userID, RepositoryParams params)
            throws NullRootException,CreateDirException{
        checkRoot(); //проверить, что rootPath!=null
        //проверить входные данные
        if(params==null) params = new RepositoryParams (); 
        //проверка на существование репозитория
        LongComparable tmpID = new LongComparable(userID);
        if(userID<0 || rParams.get(tmpID)!=null) return false;
        //создать директорию репозитория
        File newDir = new File(rootPath.getPath(),""+userID);
        if(!newDir.exists()) //проверить на существование
            if(!newDir.mkdirs()) //создать папку
                throw new CreateDirException("can't create user's directory");
        //добавить запись в хеш таблицу
        if (params == null)
            throw new NullRootException("params can't be null-value");

        rParams.putIfAbsent(tmpID, params);
        return true; 
    }

    /**
     * Запросить репозиторий пользователя
     * @param userID идентификатор пользователя
     * @return директорию репозитория или null если репозиторий отсутствует
     * @throws repository.exception.NullRootException не установлена корневая
     * директория
     */
    @Override
    public File getRepository (long userID) 
            throws NullRootException{
        checkRoot(); //проверить, что rootPath!=null
        //проверить наличие репозитория
        if(rParams.get(new LongComparable(userID))==null) return null;
        //если репозиторий существует
        return new File (rootPath.getPath(),""+userID);
    }

    /**
     * Проверить наличие репозитория
     * @param userID идентификатор пользователя
     * @return утверждение/отрицание существования репозитория
     * @throws repository.exception.NullRootException не установлена корневая
     * директория
     */
    @Override
    public boolean isRepositoryExist (long userID)
            throws NullRootException{
        checkRoot(); //проверить, что rootPath!=null
        //проверить наличие репозитория
        if(rParams.get(new LongComparable(userID))==null) return false;
        //если репозиторий существует
        return true;
    }

    /**
     * Удалить репозиторий пользователя(вместе со всеми файлами)
     * @param userID иденитификатор пользователя
     * @return true в случае успеха, false в случае ошибки
     * @throws repository.NullRootException не установлена корневая директория
     * @throws repository.exception.NoRepositoryException отсутствует репозиторий
     */
    @Override
    public boolean deleteRepository (long userID) 
            throws NullRootException,NoRepositoryException{
        checkRoot(); //проверить, что rootPath!=null
        checkRepository(userID); //проверить наличие репозитория
        //удалить запись из хэш таблицы
        rParams.remove(new LongComparable(userID));
        //удалить директорию репозитория 	      
        return recursiveDelete(getRepository(userID));
    }
    
    /**
     * Запросить все репозитории в корневой директории
     * @return массив репозиториев
     * @throws repository.exception.NullRootException не задана корневая директория
     */
    @Override
    public File[] getRepositories () throws NullRootException{
        checkRoot(); //проверить, что rootPath!=null
        //созать массив директорий
        File res[]=new File [rParams.size()];
        int i=0;
        //пройти по всем ключам
        Set <LongComparable> keys = rParams.keySet();
        for(LongComparable key : keys){
            //добавить элемент к массиву
            res[i]=new File(rootPath.getPath()+"/"+key.value);
            i++;
        }
        return res;
    }

    /**
     * Запросить файлы и директории репозитория лежащие в директории relativePath
     * @param userID иденитификатор пользователя
     * @param relativePath путь директории относительно репозитория пользователя
     * @return массив файлов, null в случае если директории не существует
     * @throws repository.exception.NullRootException корневая директория не задана
     * @throws repository.exception.NoRepositoryException отсутствует репозиторий
     */
    @Override
    public String[] getFiles (long userID, String relativePath)
            throws NullRootException,NoRepositoryException {
        //вернуть массив имен файлов
        return getFile(userID, relativePath).list();
    }

    /**
     * Запросить файлы и директории репозитория лежащие в директории relativePath
     * @param userID иденитификатор пользователя
     * @param relativePath путь директории относительно репозитория пользователя
     * @param filter фильтр файлов по имени
     * @return массив файлов, null в случае если директории не существует
     * @throws repository.exception.NullRootException корневая директория не задана
     * @throws repository.exception.NoRepositoryException отсутствует репозиторий
     */
    @Override
    public String[] getFiles(long userID,String relativePath,
            FilenameFilter filter) throws NullRootException,NoRepositoryException {
        //вернуть массив имен файлов
        return getFile(userID, relativePath).list(filter);
    }

    /**
     * Запросить файлы и директории репозитория лежащие в директории relativePath
     * @param userID иденитификатор пользователя
     * @param relativePath путь директории относительно репозитория пользователя
     * @param filter фильтр файлов
     * @return массив файлов, null в случае если директории не существует
     * @throws repository.exception.NullRootException корневая директория не задана
     * @throws repository.exception.NoRepositoryException отсутствует репозиторий
     */
    /*@Override
    public String[] getFiles(long userID,String relativePath,
            FileFilter filter) throws NullRootException,NoRepositoryException {
        checkRoot(); //проверить, что rootPath!=null
        checkRepository(userID); //проверить наличие репозитория
        if(relativePath==null) relativePath="";
        //путь: rootPath/userID/relativePath
        File dir=new File(rootPath.getPath()+File.separator+
                userID+File.separator+relativePath);
        //вернуть массив файлов
        return dir.list(filter);
    }*/

    /**
     * Запросить файл или директорию
     * @param userID иденитификатор пользователя
     * @param relativePath путь относительно репозитория пользователя
     * @return файл или директория
     * @throws repository.exception.NullRootException корневая директория не задана
     * @throws repository.exception.NoRepositoryException отсутствует репозиторий
     */
    /*public String getFile(long userID,String relativePath)
            throws NullRootException,NoRepositoryException {
        checkRoot(); //проверить, что rootPath!=null
        checkRepository(userID); //проверить наличие репозитория
        if(relativePath==null) relativePath="";
        //путь: rootPath/userID/relativePath
        File dir=new File(rootPath.getPath()+File.separator+
                userID+File.separator+relativePath);
        //вернуть массив файлов
        return dir.getName();
    }*/
    
    /**
     * Скачать файл
     * @param userID иденитификатор пользователя
     * @param relativePath путь относительно репозитория пользователя
     * @return массив байт, если файла не существует или 
     * relativePath - директория возвращает null;
     * @throws repository.exception.NullRootException корневая директория не задана
     * @throws repository.exception.NoRepositoryException отсутствует репозиторий
     * @throws java.io.IOException ошибка чтения файла
     */
    @Override
    public ByteArray downloadFile (long userID,String relativePath)
            throws IOException,NullRootException,NoRepositoryException {
        //запросить файл по относительному пути
        File f=getFile(userID, relativePath);
        //проверить на директорию
        if(f.isDirectory()) return null;
        //создать массив байт
        ByteArray array=new ByteArray((int)f.length());
        try {
            //создать файловый поток
            FileInputStream in = new FileInputStream(f);
            try{
                //побайтно считать файл
                int ch;
                while((ch=in.read())!=-1) array.write((byte) ch);
                array.chopCounter(); //сбросить счетчик
            }finally{in.close();} //закрыть поток
        } catch (FileNotFoundException ex) {  return null;}
        return array; //вернуть массив байт
    }

    /**
     * Загрузить файл в репозиторий
     * @param userID  иденитификатор пользователя
     * @param relativePath путь относительно репозитория пользователя
     * @param array массив байт
     * @return true в случае успеха, иначе false
     * @throws java.io.IOException ошибка чтения файла
     * @throws repository.exception.NullRootException корневая директория не задана
     * @throws repository.exception.NoRepositoryException отсутствует репозиторий
     * @throws repository.exception.FileSizeException превышен размер файла
     * @throws repository.exception.RepositorySizeException превышен размер репозитория
     */
    @Override
    public boolean uploadFile(long userID,String relativePath, ByteArray array)
            throws IOException,NullRootException,NoRepositoryException,
            FileSizeException,RepositorySizeException {
        //проверить на null
        if (array==null) array=new ByteArray(0);
        //запросить файл по относительному пути
        File f=getFile(userID, relativePath);
        //проверить на директорию
        if(f.isDirectory()) return false;
        //проверить размер
        RepositoryParams param = rParams.get(new LongComparable(userID));
        //проверить размер файла
        if(param.getMaxFileSize()!=-1 && array.size()>param.getMaxFileSize())
            throw new FileSizeException("File "+relativePath+" too big");
        //проверить размер репозитория
        if(param.getMaxSize()!=-1 && innerGetSize(new File(rootPath,""+userID))
                -f.length()+array.size()>param.getMaxSize())
            throw new RepositorySizeException("Not enoth plase in repository");
        //создать файл
        try {
            //создать файловый поток
            FileOutputStream out = new FileOutputStream(f);
            try{
                //записать файл
                out.write(array.getAll());
            }finally{out.close();} //закрыть поток
        } catch (FileNotFoundException ex) {  return false;}
        return true;
    }

    /**
     * Проверить явяется ли файл с заданным именем директорией
     * @param userID иденитификатор пользователя
     * @param relativePath путь относительно репозитория пользователя
     * @return true если директория
     * @throws repository.exception.NullRootException корневая директория не задана
     * @throws repository.exception.NoRepositoryException отсутствует репозиторий
     */
    @Override
    public boolean isDirectory(long userID,String relativePath)
            throws NullRootException,NoRepositoryException {
        //проверить, явяется ли директорией
        return getFile(userID, relativePath).isDirectory();
    }

    /**
     * Добавить файл в репозиторий
     * @param userID иденитификатор пользователя
     * @param file добавляемый файл
     * @param relativePath путь относительно репозитория пользователя
     * @return ture - в случае успеха, false - файл существует
     * @throws NullRootException  корневая директория не задана
     * @throws IOException ошибка ввода/вывода при копировании файла
     * @throws NoRepositoryException отсутствует репозиторий
     * @throws FileSizeException превышен размер файла
     * @throws RepositorySizeException превышен размер репозитория
     */
    /*@Override
    public boolean createFile (long userID, File file, String relativePath)
            throws NullRootException,NoRepositoryException, IOException,
            FileSizeException,RepositorySizeException{
        checkRoot(); //проверить, что rootPath!=null
        if(relativePath==null) relativePath="";
        //проверка наличия репоитория
        RepositoryParams param = rParams.get(new LongComparable(userID));
        if(param == null)
            throw new NoRepositoryException("Repository doesn't exist");
        //проверка размера файла
        if(param.getMaxFileSize() < file.length() && param.getMaxFileSize()!=-1)
            throw new FileSizeException("Too big file size");
        //проверка размера репозитория
        if(param.getMaxSize() < file.length() +
                innerGetSize(getRepository(userID)) && param.getMaxSize()!=-1)
            throw new RepositorySizeException("No space in repository");
        //копировать файл
    	File destination=new File(getRepository(userID),relativePath);
    	if(destination.exists()) return false;
    	copy(file,destination );
        return true;
    }*/

    /**
     * Создать папку в директории репозитория. при необходимость создает
     * родительские папки
     * @param userID иденитификатор пользователя
     * @param relativePath путь относительно репозитория пользователя
     * @return ture - в случае успеха, иначе false
     * @throws repository.exception.NullRootException корневая директория не задана
     * @throws repository.exception.NoRepositoryException отсутствует репозиторий
     */
    @Override
    public boolean createDirectory(long userID, String relativePath)
            throws NullRootException, IOException, NoRepositoryException {
        checkRoot(); //проверить, что rootPath!=null
        checkRepository(userID); //проверить наличие репозитория
        if(relativePath==null) relativePath="";
        //создать директорию
        File destination=new File(getRepository(userID),relativePath);
        //создать директорию
        return destination.mkdirs();
    }

    /**
     * Удалить файл или директорию из репозитория.
     * Для удаления директории она должна быть пуста.
     * @param userID иденитификатор пользователя
     * @param relativePath путь к файлу относительно репозитория
     * @return true - успешное удаление, false в противном случае
     * @throws NullRootException корневая директория не задана
     * @throws repository.exception.NoRepositoryException нет репозитория
     */
    @Override
    public boolean deleteFile (long userID, String relativePath)
            throws NullRootException, NoRepositoryException {
        //удалить файл
        return getFile(userID, relativePath).delete();
    }
    
    /**
     * Удалить файл или директорию из репозитория.
     * В случае удаления директории удяляется и её содержимое.
     * @param userID иденитификатор пользователя
     * @param relativePath путь к файлу относительно репозитория
     * @return true - успешное удаление, false в противном случае
     * @throws NullRootException корневая директория не задана
     * @throws repository.exception.NoRepositoryException нет репозитория
     */
    public boolean recurrenceDelete (long userID, String relativePath)
            throws NullRootException, NoRepositoryException {
        //рекурсивно удалить файл
    	return recursiveDelete(getFile(userID, relativePath));
    }

    /**
     * Обновить файл репозитория
     * @param userID иденитификатор пользователя
     * @param file файл-источник
     * @param relativePath путь назначения относительно репозитория пользователя
     * @throws NullRootException корневая директория не задана
     * @throws IOException ошибка ввода/вывода при копировании файла
     * @throws FileSizeException превышен размер файла
     * @throws RepositorySizeException превышен размер репозитория
     */
    /*@Override
    public void updateFile (long userID, File file, String relativePath)
            throws NullRootException, IOException, 
            NoRepositoryException,FileSizeException,RepositorySizeException{
    	checkRoot(); //проверить, что rootPath!=null
        if(relativePath==null) relativePath="";
        //проверка наличия репоитория
        RepositoryParams param = rParams.get(new LongComparable(userID));
        if(param == null)
            throw new NoRepositoryException("Repository doesn't exist");
        //проверка размера файла
        if(param.getMaxFileSize() < file.length() && param.getMaxFileSize()!=-1)
            throw new FileSizeException("Too big file size");
        //проверка размера репозитория
        File destination=new File(getRepository(userID),relativePath);
        if(param.getMaxSize() - destination.length() < file.length() +
                innerGetSize(getRepository(userID)) && param.getMaxSize()!=-1)
            throw new RepositorySizeException("No space in repository");
        //копировать файл
    	copy(file,destination );
    }*/

    /**
     * Переименовать файл. Можно использовать для перемещения файла
     * @param userID иденитификатор пользователя
     * @param relativePath путь относительно репозитория пользователя
     * @param newRelativePath новый путь относительно репозитория пользователя
     * @return true в случае успеха, иначе false
     * @throws NullRootException корневая директория не задана
     * @throws NoRepositoryException нет репозитория указанного пользователя
     */
    @Override
    public boolean renameFile(long userID, String relativePath,
            String newRelativePath) throws NullRootException, NoRepositoryException{
        //переименовать
        if(newRelativePath==null) newRelativePath="";
        File destFile=new File(rootPath.getPath()+File.separator+
                userID+File.separator+newRelativePath);
    	return getFile(userID, relativePath).renameTo(destFile);
    }

    /**
     * Провенрить существование файла или директории
     * @param userID иденитификатор пользователя
     * @param relativePath путь относительно репозитория пользователя
     * @return true в случае успеха, иначе false
     * @throws NullRootException корневая директория не задана
     * @throws NoRepositoryException нет репозитория указанного пользователя
     */
    public boolean exist(long userID, String relativePath)
            throws NullRootException, NoRepositoryException{
        //проверить существование
        return getFile(userID, relativePath).exists();
    }

    /**
     * Запросить время создания файла
     * @param userID иденитификатор пользователя
     * @param relativePath путь относительно репозитория пользователя
     * @return время в милисекунах с 1970 года
     * @throws NullRootException корневая директория не задана
     * @throws NoRepositoryException нет репозитория указанного пользователя
     */
    public long lastModified(long userID, String relativePath)
            throws NullRootException, NoRepositoryException{
        //вернуть время создания
        return getFile(userID, relativePath).lastModified();
    }

    /**
     * Запросить количество файлов в папке и подпапках
     * @param userID иденитификатор пользователя
     * @param relativePath путь относительно репозитория пользователя
     * @return число файлов в папке и подпапках. -1 если на relativePath не
     * существует или является файлом
     * @throws NullRootException корневая директория не задана
     * @throws NoRepositoryException нет репозитория указанного пользователя
     */
    public int getFilesNumber(long userID, String relativePath)
            throws NullRootException, NoRepositoryException{
        //запросить файл по относительному пути
        File f=getFile(userID, relativePath);
        //проверить на директорию
        if(!f.exists() || f.isDirectory()) return -1;
        return innerGetFileNum(f);
    }

    /**
     * запросить размер файла или директории
     * @param userID иденитификатор пользователя
     * @param relativePath путь относительно репозитория пользователя
     * @return размер файла или директории в байтах
     * @throws NullRootException корневая директория не задана
     * @throws NoRepositoryException нет репозитория указанного пользователя
     */
    public long getSize(long userID, String relativePath)
            throws NullRootException, NoRepositoryException{
        //запросить размер файла или директории
        return innerGetSize(getFile(userID, relativePath));
    }

    /**
     * Запросить размер корневой директории
     * @return размер директории в байтах
     * @throws repository.exception.NullRootException корневая директория не 
     * задана
     */
    @Override
    public long getRootSize () throws NullRootException {
        checkRoot(); //проверить, что rootPath!=null
        return innerGetSize(rootPath);
    }

    /**
     * Запросить размер репозитория
     * @param userID идентификатор пользователя
     * @return размер репозитория в байтах
     * @throws repository.exception.NullRootException корневая директория не задана
     * @throws NoRepositoryException нет репозитория указанного пользователя
     */
    @Override
    public long getRepositorySize (long userID) 
            throws NullRootException, NoRepositoryException {
        return innerGetSize(getFile(userID, ""));
    }
    
    /**
     * Запросить параметры репозитория
     * @param userID идентификатор пользователя
     * @return параметры репозитория, если репозиторий отсутствует - null
     */
    @Override
    public RepositoryParams getRepositoryParams (long userID){
        //создать оболочку
        LongComparable tmpID = new LongComparable(userID);
        return rParams.get(tmpID);
    }

    /**
     * Установить параметры репозитория
     * @param userID идентификатор пользователя
     * @param params параметры репозитория. Если пкрдать null - будут заданы
     * пустые параметры
     * @throws repository.exception.NoRepositoryException отсутствует 
     * репозиторий заданного пользователя
     */
    @Override
    public void setRepositoryParams (long userID, RepositoryParams params) 
            throws NoRepositoryException{
        //проверить наличие репозитория
        LongComparable tmpID = new LongComparable(userID);
        RepositoryParams tmpParamsOld=rParams.get(tmpID);
        if(tmpParamsOld==null)
            throw new NoRepositoryException("User hasn't repository");
        //проверка параметров
        RepositoryParams tmpParams=params;
        if(tmpParams==null) tmpParams=new RepositoryParams();
        //установить параметры
        rParams.replace(tmpID, tmpParamsOld, tmpParams);
    }

    /**
     * Прочитать параметры из файла "repository.data" в директорию приложения
     * @throws repository.exception.DataFileException ошибка при чтении файла
     */
    @Override
    public void loadRepositoryData () throws DataFileException{
        //работа с файлом
        try{
            //создать поток
            ObjectInputStream stream = new ObjectInputStream
                (new FileInputStream("./repository.data"));
            //загрузить
            try{
                //удалить текущие значения
                rParams.clear();
                //прочитать контрольную сумму
                int size = stream.readInt();
                //объекты для ключа и значений хэш таблицы
                LongComparable tmpKey;
                RepositoryParams tmpValue;
                //прочитать все записи из файла
                for(int i=0;i<size;i++){
                    //прочитать Id поьзователя и параметры репозитория
                    tmpKey=new LongComparable(stream.readLong());
                    tmpValue=new RepositoryParams
                            (stream.readLong(),stream.readLong());
                    //добавить запись в хэш таблицу
                    rParams.putIfAbsent(tmpKey, tmpValue);
                }
                System.out.println("REPOSITORY: loaded "+rParams.size()+" units");
            }
            
            //закрыть файл
            finally{ stream.close(); }
        }
        catch(Exception e){
            throw new DataFileException("Can't load data");
        }
    }

    /**
     * Сохраняет параметры в файл "repository.data" в директорию приложения
     * формат файла: [число_записей][UserID|MaxSize|MaxFileSize]...
     * @throws repository.exception.DataFileException ошибка при записи в файл
     */
    @Override
    public void saveRepositoryData () throws DataFileException{
        //работа с файлом
        try{
            //создать поток
            ObjectOutputStream stream = new ObjectOutputStream
                (new FileOutputStream("./repository.data"));
            //сохранить записи
            try{
                //записать контрольную сумму
                stream.writeInt(rParams.size());
                //объекты для ключа и данных хэш таблицы
                LongComparable tmpKey;
                RepositoryParams tmpValue;
                //пройти по всем элементам хэш таблицы параметров
                for(Map.Entry<LongComparable,RepositoryParams> entry :
                    rParams.entrySet()){
                        //запроисть ключ и значение
                        tmpKey=entry.getKey();
                        tmpValue=entry.getValue();
                        //записать Id пользователя и параметры его репозитория
                        stream.writeLong(tmpKey.value);
                        stream.writeLong(tmpValue.getMaxSize());
                        stream.writeLong(tmpValue.getMaxFileSize());
                    }
                //запись в файл
                stream.flush();
            }
            //закрыть файл
            finally{ stream.close(); }
        }
        catch(Exception e){
            throw new DataFileException("Can't save data");
        }
    }

}
